using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Diagnostics;
using System.ComponentModel;

namespace PassThePuzzle
{
    public partial class SettingsDlg : Form
    {
        private class FileListItem
        {
            public string Filename;
            public override string ToString()
            {
                if (Filename.EndsWith(_categoryFileExtension))
                {
                    return Filename.Substring(0, Filename.Length - _categoryFileExtension.Length);
                }
                else if (Filename.EndsWith(_categoryGzipFileExtension))
                {
                    return Filename.Substring(0, Filename.Length - _categoryGzipFileExtension.Length);
                }
                else
                {
                    return Filename;
                }
            }
        }

        private const string _categoryFileExtension = ".ptp";
        private const string _categoryGzipFileExtension = ".ptp.gz";
        private const string _settingsFileExtension = ".ptp.xml";
        private const string _currentSettingsBasename = "settings";
        private const int _showScoresWidth = 15;
        private Settings _settings;
        //private Dictionary<string, float> _scores;
        private string _categoryNames;
        private string[] _categoryMembers;

        public SettingsDlg()
        {
            InitializeComponent();
            InitializeDefaultValues();
        }

        private void InitializeDefaultValues()
        {
            refreshCategoryFiles();
            readSettingsFromFile();
            //comboBoxLetters.Items.Add(comboBoxLetters.Text);
            showLetterDistribution();
            ShowScores();
        }

        private void buttonLoadSettings_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Title = "Load settings file";
            openFileDialog.Filter = "Settings file|*" + _settingsFileExtension;
            openFileDialog.InitialDirectory = ".";
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                readSettingsFromFile(openFileDialog.FileName.
                  Substring(0, openFileDialog.FileName.Length - _settingsFileExtension.Length));
            }
        }

        private void buttonSaveSettings_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.Title = "Save settings file";
            saveFileDialog.Filter = "Settings file|*" + _settingsFileExtension;
            saveFileDialog.InitialDirectory = ".";
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                writeSettingsToFile(saveFileDialog.FileName.
                  Substring(0, saveFileDialog.FileName.Length - _settingsFileExtension.Length));
            }
        }

        private bool writeSettingsToFile()
        {
            if (_settings == null)
                _settings = new Settings();
            _settings.letters = comboBoxLetters.Text;
            _settings.puzzleSizeMin = (int) ctlPuzzleSizeMin.Value;
            _settings.puzzleSizeMax = (int) ctlPuzzleSizeMax.Value;
            _settings.roundTimeMin = (int) ctlRoundTimeMin.Value;
            _settings.roundTimeMax = (int) ctlRoundTimeMax.Value;
            _settings.solutionCountMin = (int) ctlSolutionsMin.Value;
            _settings.solutionCountMax = (int) ctlSolutionsMax.Value;
            _settings.showSolutionCount = optShowSolutionCount.Checked;
            _settings.lettersBeginWith = optBeginWith.Checked;
            _settings.lettersEndWith = optEndWith.Checked;
            _settings.lettersGivenOrder = optGivenOrder.Checked;
            _settings.lettersConsecutive = optConsecutive.Checked;
            _settings.keepScores = optKeepScores.Checked;
            _settings.categoryFiles = lstCategories.SelectedItems.Cast<FileListItem>().Select(item => item.Filename).ToArray();
            return writeSettingsToFile(_currentSettingsBasename);
        }

        private bool writeSettingsToFile(string basename)
        {
            string filename = basename + _settingsFileExtension;
            try
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(Settings));
                TextWriter textWriter = new StreamWriter(filename);
                xmlSerializer.Serialize(textWriter, _settings);
                textWriter.Close();
                return true;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
                return false;
            }
        }

        private bool readSettingsFromFile()
        {
            if (readSettingsFromFile(_currentSettingsBasename))
            {
                setSettingsToForm();
                return true;
            }
            else
            {
                return false;
            }
        }

        private bool readSettingsFromFile(string basename)
        {
            string filename = basename + _settingsFileExtension;
            if (File.Exists(filename))
            {
                try
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(typeof(Settings));
                    TextReader textReader = new StreamReader(filename);
                    _settings = (Settings) xmlSerializer.Deserialize(textReader);
                    textReader.Close();
                    return true;
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message);
                    return false;
                }
            }
            return false;
        }

        private void setSettingsToForm()
        {
            comboBoxLetters.Text = _settings.letters;
            ctlPuzzleSizeMin.Value = _settings.puzzleSizeMin;
            ctlPuzzleSizeMax.Value = _settings.puzzleSizeMax;
            ctlRoundTimeMin.Value = _settings.roundTimeMin;
            ctlRoundTimeMax.Value = _settings.roundTimeMax;
            ctlSolutionsMin.Value = _settings.solutionCountMin;
            ctlSolutionsMax.Value = _settings.solutionCountMax;
            optShowSolutionCount.Checked = _settings.showSolutionCount;
            optBeginWith.Checked = _settings.lettersBeginWith;
            optEndWith.Checked = _settings.lettersEndWith;
            optGivenOrder.Checked = _settings.lettersGivenOrder;
            optConsecutive.Checked = _settings.lettersConsecutive;
            optKeepScores.Checked = _settings.keepScores;

            if (_settings.categoryFiles != null)
            {
                string[] listboxFiles = lstCategories.Items.Cast<FileListItem>().Select(i => i.Filename).ToArray();
                bool listboxFileIsInSettings;
                for (int i = 0; i < listboxFiles.Length; i++)
                {
                    listboxFileIsInSettings = false;
                    foreach (string settingsFile in _settings.categoryFiles)
                    {
                        if (settingsFile.Equals(listboxFiles[i]))
                        {
                            listboxFileIsInSettings = true;
                            break;
                        }
                    }
                    lstCategories.SetSelected(i, listboxFileIsInSettings);
                }
            }
        }

        private void buttonExit_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void buttonPlay_Click(object sender, EventArgs e)
        {
            writeSettingsToFile();
            readCategoryMembers();
            Program.gameform.Show();
            Hide();
            Program.gameform.StartGame(_settings, _categoryNames, _categoryMembers);
        }

        private void buttonClearScores_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Clear scores?", "Clear scores?", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                _settings.RemoveAllScores();
                ShowScores();
            }
        }

        public void ShowScores()
        {
            string msg = "SCORES";
            string nameShown;
            int nameLengthMax = 0;
            int valueLengthMax = 0;
            int valueLength;
            if (_settings != null && _settings.scores != null && _settings.scores.Count > 0)
            {
                foreach (score score in _settings.scores)
                {
                    valueLength = score.value.ToString().Length;
                    if (valueLength > valueLengthMax)
                    {
                        valueLengthMax = valueLength;
                    }
                }
                nameLengthMax = _showScoresWidth - valueLengthMax - 1;
                foreach (score score in _settings.scores)
                {
                    nameShown = score.name;
                    if (nameShown.Length > nameLengthMax)
                    {
                        nameShown = nameShown.Substring(0, nameLengthMax - 1) + "\u2026";
                    }
                    msg += "\r\n" + nameShown.PadRight(nameLengthMax) +
                      " " + score.value.ToString().PadLeft(valueLengthMax);
                }
                buttonClearScores.Enabled = true;
            }
            else
            {
                msg = "NO SCORES\r\nRECORDED.";
                buttonClearScores.Enabled = false;
            }
            txtScores.Text = msg;
        }

        private void showLetterDistribution()
        {
            Dictionary<char, int> LetterDic = new Dictionary<char, int>();
            string letters = comboBoxLetters.Text;
            string distribution = "";
            char chr;
            for (int i = 0; i < letters.Length; i++)
            {
                chr = letters[i];
                LetterDic[chr] = (LetterDic.ContainsKey(chr) ? LetterDic[chr] + 1 : 1);
            }
            foreach (KeyValuePair<char, int> kvp in LetterDic)
            {
                distribution += kvp.Key + ":" + Utils.nbsp + kvp.Value + "\t";
            }
            txtLetterDistribution.Text = distribution;
        }

        private void comboBoxLetters_TextUpdate(object sender, EventArgs e)
        {
            showLetterDistribution();
        }

        private void refreshCategoryFiles()
        {
            lstCategories.Items.Clear();

            DirectoryInfo directory = new DirectoryInfo(".");
            var categoryFiles = directory.GetFiles("*" + _categoryFileExtension).
              Concat(directory.GetFiles("*" + _categoryGzipFileExtension));
            foreach (FileInfo f in categoryFiles.OrderBy(
              f => Path.GetFileNameWithoutExtension(f.Name)))
            {
                lstCategories.Items.Add(new FileListItem() { Filename = f.Name });
            }
        }

        private void readCategoryMembers()
        {
            _categoryNames = string.Join(", ",
              lstCategories.SelectedItems.Cast<FileListItem>().Select(i => i.ToString()).ToArray());
            var categoryMembersList = new List<string>();
            foreach (FileListItem item in lstCategories.SelectedItems)
            {
                if (item.Filename.EndsWith(_categoryGzipFileExtension))
                { // gzip file
                    try
                    {
                        using (FileStream filestream = File.Open(item.Filename, FileMode.Open, FileAccess.Read))
                        {
                            using (GZipStream gzipstream = new GZipStream(filestream, CompressionMode.Decompress))
                            {
                                using (StreamReader streamreader = new StreamReader(gzipstream))
                                {
                                    categoryMembersList.AddRange(streamreader.ReadToEnd().Replace("\r", "").Split('\n'));
                                    streamreader.Close();
                                    gzipstream.Close();
                                    filestream.Close();
                                }
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(e.Message);
                    }
                }
                else
                { // plain text file
                    try
                    {
                        categoryMembersList.AddRange(File.ReadAllLines(item.Filename));
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(e.Message);
                    }
                }
            }
            _categoryMembers = categoryMembersList.ToArray();
        }

        private void SettingsDlg_FormClosing(object sender, FormClosingEventArgs e)
        {
            writeSettingsToFile();
        }

        private void btnHelp_Click(object sender, EventArgs e)
        {
            try
            {
                Process.Start(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "readme.txt"));
            }
            catch (Win32Exception)
            {
                MessageBox.Show(@"The file ""readme.txt"" could not be found. Ensure that the file is located in the same folder as the program and try again.");
            }
        }
    }
}
